using System;
using Microsoft.SPOT;

namespace nRF24L10P
{
    public class DataReadPipe
    {
        nRF24L10P device;
        public DataReadPipe(nRF24L10P device, int id)
        {
            this.device = device;
            ID = id;
        }
        public int ID { get; private set; }
        public byte[] Address
        {
            get
            {
                switch (ID)
                {
                    case 0:
                        return device.Registers.RX_ADDR_P0;
                    case 1:
                        return device.Registers.RX_ADDR_P1;
                    case 2:
                        return device.Registers.RX_ADDR_P2;
                    case 3:
                        return device.Registers.RX_ADDR_P3;
                    case 4:
                        return device.Registers.RX_ADDR_P4;
                    case 5:
                        return device.Registers.RX_ADDR_P5;
                    default:
                        throw new InvalidOperationException("Cannot get address for data pipe, invalid ID number for this Pipe");
                }
            }
            set
            {
                if (ID<2 && value.Length!=device.Config.AddressWidth)
                {
                    throw new InvalidOperationException("Address length should equal to device.Config.Addresswidth");
                }
                if (ID>1 && value.Length!=1)
                {
                    throw new InvalidOperationException("Address length should be 1 byte for receive pipes 2 to 5");
                }
                switch (ID)
                {
                    case 0:
                        device.Registers.RX_ADDR_P0.Value = value;
                        device.Registers.RX_ADDR_P0.Save();
                        break;
                    case 1:
                        device.Registers.RX_ADDR_P1.Value = value;
                        device.Registers.RX_ADDR_P1.Save();
                        break;
                    case 2:
                        device.Registers.RX_ADDR_P2.Value = value;
                        device.Registers.RX_ADDR_P2.Save();
                        break;
                    case 3:
                        device.Registers.RX_ADDR_P3.Value = value;
                        device.Registers.RX_ADDR_P3.Save();
                        break;
                    case 4:
                        device.Registers.RX_ADDR_P4.Value = value;
                        device.Registers.RX_ADDR_P4.Save();
                        break;
                    case 5:
                        device.Registers.RX_ADDR_P5.Value = value;
                        device.Registers.RX_ADDR_P5.Save();
                        break;
                    default:
                        throw new InvalidOperationException("Cannot set address for data pipe, invalid ID number for this Pipe");
                }
            }
        }
        public bool Enabled
        {
            get
            {
                switch (ID)
                {
                    case 0:
                        return device.Registers.EN_RXADDR.ERX_P0;
                    case 1:
                        return device.Registers.EN_RXADDR.ERX_P1;
                    case 2:
                        return device.Registers.EN_RXADDR.ERX_P2;
                    case 3:
                        return device.Registers.EN_RXADDR.ERX_P3;
                    case 4:
                        return device.Registers.EN_RXADDR.ERX_P4;
                    case 5:
                        return device.Registers.EN_RXADDR.ERX_P5;
                    default:
                        throw new InvalidOperationException("Cannot get register value, invalid ID number for this Pipe");
                }

            }
            set
            {
                switch (ID)
                {
                    case 0:
                        device.Registers.EN_RXADDR.ERX_P0 = value;
                        device.Registers.EN_RXADDR.Save();
                        break;
                    case 1:
                        device.Registers.EN_RXADDR.ERX_P1 = value;
                        device.Registers.EN_RXADDR.Save();
                        break;
                    case 2:
                        device.Registers.EN_RXADDR.ERX_P2 = value;
                        device.Registers.EN_RXADDR.Save();
                        break;
                    case 3:
                        device.Registers.EN_RXADDR.ERX_P3 = value;
                        device.Registers.EN_RXADDR.Save();
                        break;
                    case 4:
                        device.Registers.EN_RXADDR.ERX_P4 = value;
                        device.Registers.EN_RXADDR.Save();
                        break;
                    case 5:
                        device.Registers.EN_RXADDR.ERX_P5 = value;
                        device.Registers.EN_RXADDR.Save();
                        break;
                    default:
                        throw new InvalidOperationException("Cannot set register value, invalid ID number for this Pipe");
                }

            }

        }
        public bool AutoACKEnabled
        {
            get
            {
                switch (ID)
                {
                    case 0:
                        return device.Registers.EN_AA.ENAA_P0;
                    case 1:
                        return device.Registers.EN_AA.ENAA_P1;
                    case 2:
                        return device.Registers.EN_AA.ENAA_P2;
                    case 3:
                        return device.Registers.EN_AA.ENAA_P3;
                    case 4:
                        return device.Registers.EN_AA.ENAA_P4;
                    case 5:
                        return device.Registers.EN_AA.ENAA_P5;
                    default:
                        throw new InvalidOperationException("Cannot get DynamicPayloadEnabled for data pipe, invalid ID number for this Pipe");
                }
            }
            set
            {
                switch (ID)
                {
                    case 0:
                        device.Registers.EN_AA.ENAA_P0 = value;
                        device.Registers.EN_AA.Save();
                        break;
                    case 1:
                        device.Registers.EN_AA.ENAA_P1 = value;
                        device.Registers.EN_AA.Save();
                        break;
                    case 2:
                        device.Registers.EN_AA.ENAA_P2 = value;
                        device.Registers.EN_AA.Save();
                        break;
                    case 3:
                        device.Registers.EN_AA.ENAA_P3 = value;
                        device.Registers.EN_AA.Save();
                        break;
                    case 4:
                        device.Registers.EN_AA.ENAA_P4 = value;
                        device.Registers.EN_AA.Save();
                        break;
                    case 5:
                        device.Registers.EN_AA.ENAA_P5 = value;
                        device.Registers.EN_AA.Save();
                        break;
                    default:
                        throw new InvalidOperationException("Cannot set DynamicPayloadEnabled for data pipe, invalid ID number for this Pipe");
                }
            }
        }
        public byte BytesToRead
        {
            get
            {
                return device.SpiReadWrite(SPICommands.R_RX_PL_WID,nRF24L10P.EMPTY_ADDRESS,new byte[1])[0];
            }
        }
        public bool DynamicPayloadLengthEnabled
        {
            get
            {
                switch (ID)
                {
                    case 0:
                        return device.Registers.DYNPD.DPL_P0;
                    case 1:
                        return device.Registers.DYNPD.DPL_P1;
                    case 2:
                        return device.Registers.DYNPD.DPL_P2;
                    case 3:
                        return device.Registers.DYNPD.DPL_P3;
                    case 4:
                        return device.Registers.DYNPD.DPL_P4;
                    case 5:
                        return device.Registers.DYNPD.DPL_P5;
                    default:
                        throw new InvalidOperationException("Cannot get DynamicPayloadLengthEnabled for data pipe, invalid ID number for this Pipe");
                }
            }
            set
            {
                if (value && !device.Config.DynamicPayloadLengthEnabled)
                {
                    throw new InvalidOperationException("please enable Config.DynamicPayloadLengthEnabled before you enable this feature on data pipe");
                }
                if (value && !AutoACKEnabled)
                {
                    throw new InvalidOperationException("please enable AutoACK of current data pipe before you can enable this feature on current data pipe");
                }
                switch (ID)
                {
                    case 0:
                        device.Registers.DYNPD.DPL_P0 = value;
                        device.Registers.DYNPD.Save();
                        break;
                    case 1:
                        device.Registers.DYNPD.DPL_P1 = value;
                        device.Registers.DYNPD.Save();
                        break;
                    case 2:
                        device.Registers.DYNPD.DPL_P2 = value;
                        device.Registers.DYNPD.Save();
                        break;
                    case 3:
                        device.Registers.DYNPD.DPL_P3 = value;
                        device.Registers.DYNPD.Save();
                        break;
                    case 4:
                        device.Registers.DYNPD.DPL_P4 = value;
                        device.Registers.DYNPD.Save();
                        break;
                    case 5:
                        device.Registers.DYNPD.DPL_P5 = value;
                        device.Registers.DYNPD.Save();
                        break;
                    default:
                        throw new InvalidOperationException("Cannot set DynamicPayloadLengthEnabled for data pipe, invalid ID number for this Pipe");
                }
            }
        }

        /// <summary>
        /// Reads data from RX buffer, use Dynamic payload length feature to detect package length
        /// </summary>
        /// <returns></returns>
        public byte[] ReadBufferAll(int readBufferSize=512)
        {
            byte[] buffer = new byte[readBufferSize]; // 512 bytes receive buffer by default

            int length = ReadBufferAll(buffer, 0);
            byte[] result = new byte[length];
            Array.Copy(buffer, result, length);
            return result;
        }

        /// <summary>
        /// Reads data from RX buffer, use this feature when dynamic payload length is turned off
        /// Warning: use this feature will ignore the data pipe ID, it may read buffer belongs to other data pipe
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public byte[] ReadBuffer(int length)
        {
            if (length == 0 || length > 32)
            {
                throw new ArgumentOutOfRangeException("length", "length should between 1 and 32");
            }

            byte[] buffer = new byte[length]; // place holder for SPI communication

            return device.SpiReadWrite(SPICommands.R_RX_PAYLOAD, nRF24L10P.EMPTY_ADDRESS, buffer,false);
        }


        /// <summary>
        /// reads all data available in FIFO buffer to a byte array
        /// </summary>
        /// <param name="outputBuffer">output buffer</param>
        /// <param name="writeStartPosition">start position in output buffer</param>
        /// <returns></returns>
        public int ReadBufferAll(byte[] outputBuffer,int writeStartPosition)
        {
            int position = writeStartPosition;
            int bytesRead = 0;
            if (DynamicPayloadLengthEnabled)
            {
                device.Registers.FIFO_STATUS.Load();
                while (!device.Registers.FIFO_STATUS.RX_EMPTY )
                {
                    int length = this.BytesToRead;
                    if (length > 32)//incorrect RX package 
                    {
                        device.FlushRX();
                        return 0;
                    }
                    if (length == 0)
                    {
                        return 0;
                    }
                    if (position+length>outputBuffer.Length)//buffer full
                    {
                        break;
                    }
                    Array.Copy(ReadBuffer(length), 0, outputBuffer, position, length);
                    position += length;
                    bytesRead += length;
                    device.Registers.FIFO_STATUS.Load();
                }
                return bytesRead;
            }
            throw new InvalidOperationException("DynamicPayloadLength is not enabled on this pipe, please turn it On or use ReadBuffer(int length) instead");
        }
    }
}
